<?php

namespace App\Admin\Form\Field;

use App\Admin\Form\Field;
use Illuminate\Http\UploadedFile;
use App\Models\File as FileManager;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;

class File extends Field
{
    /**
     * Js
     *
     * @var array
     */
    protected static $js = [
        'assets/plupload/plupload.full.min.js',
    ];

    /**
     * 上传目录(是在Storage某个驱动之后的目录,不是完整目录)
     *
     * @var string
     */
    protected $directory = '';

    /**
     * 文件系统驱动配置
     *
     * @var array
     */
    protected $diskConfig = [];

    /**
     * 文件存储名
     *
     * @var \Closure|null|string
     */
    protected $name = null;

    /**
     * Storage实例
     *
     * @var \Illuminate\Filesystem\Filesystem
     */
    protected $storage = '';

    /**
     * 是否使用生成唯一文件名
     *
     * @var bool
     */
    protected $useUniqueName = true;

    /**
     * 是否是多文件组件
     *
     * @var bool
     */
    protected $isMultiple = false;

    /**
     * Create a new File instance.
     *
     * @param string $column
     * @param array  $arguments
     */
    public function __construct($column, $arguments = [])
    {
        $this->initStorage();
        $this->setupDefaultOptions();

        parent::__construct($column, $arguments);
    }

    /**
     * 存储文件,返回文件url,并删除记录被更新前的文件
     *
     * @param $fieldName
     *
     * @return null|array
     */
    public function uploadAndDeleteOriginal($fieldName)
    {
        /**
         * @var \Illuminate\Http\UploadedFile $file
         */
        $file = \Plupload::receive($fieldName);
        if (is_null($file)) {
            return null;
        }

        $this->name = $this->getStoreName($file);
        $this->renameIfExists($file);

        $target = rtrim($this->rootDir(), '/\\').'/'.ltrim($this->getDirectory(), '/\\');

        $file->move($target, $this->name);

        $this->destroy();

        return $this->handleFileManager($file);
    }

    /**
     * 文件上传后,插入一条记录到files表中
     *
     * @param \Illuminate\Http\UploadedFile $file
     *
     * @return array
     */
    protected function handleFileManager($file)
    {
        $filePath = $this->url($this->getDirectory().'/'.$this->name);

        // $fileRec = FileManager::create([
        //     'name' => $this->name,
        //     'path' => $filePath,
        //     'ext'  => $file->getExtension(),
        // ]);

        return [
            'src'   => $filePath,
            'value' => $filePath,
        ];
    }

    /**
     * @inheritdoc
     */
    public function additionalVariables()
    {
        if (empty($this->options)) {
            $options = '{}';
        } else {
            $options = json_encode($this->options);
        }

        $multiple = $this->isMultiple;

        $name = $this->variables['name'];
        $fieldName = $this->isMultiple ? ($name.'[]') : $name;
        $validateName = $this->isMultiple ? "{$name}.*|{$name}" : $name;

        return compact('options', 'multiple', 'fieldName', 'validateName');
    }

    /**
     * 初始化组件的默认值
     */
    protected function setUpDefaultValue()
    {
        if (is_null($this->default)) {
            if ($this->isMultiple) {
                $this->default = [];
            } else {
                $this->default = '';
            }
        }
    }

    public function getDefault()
    {
        $this->setUpDefaultValue();

        return parent::getDefault();
    }

    public function fill($data)
    {
        $this->value = array_get($data, $this->column);

        // 各种情况下,数据的格式化
        if ($this->isMultiple) {
            $this->value = $this->value
                ? array_map(function ($file) {
                    return $this->assembleFieldValue($file['path'], $file['id']);
                }, $this->value)
                : [];

            return;
        }

        if (!$this->isMultiple) {
            $this->value = $this->value
                ? [$this->assembleFieldValue($this->value, $this->value),]
                : [];

            return;
        }
    }

    public function assembleFieldValue($src, $value)
    {
        try {
            $file = new UploadedFile(public_path($src), '');
        } catch (FileNotFoundException $e) {
            $file = null;
        }

        return [
            'src'      => $src,
            'value'    => $value,
            'is_image' => $file ? $this->isImage($file->getMimeType()) : false,
            'ext'      => $file ? $file->getExtension() : '丢失',
        ];
    }

    protected function isImage($mimeType)
    {
        return strpos($mimeType, 'image') !== false;
    }

    /**
     * 设置默认的上传驱动
     *
     * @return void.
     */
    protected function initStorage()
    {
        $this->disk('admin');
    }

    /**
     * 默认的前端上传插件设置项
     *
     * @return void
     */
    protected function setupDefaultOptions()
    {
        $defaultOptions = [
            'max_num'          => 1,
            'runtimes'         => 'html5,flash',
            // 这里由于构造函数中,还没有设置form实例,
            // 'url'              => $this->form->builder()->getAction(),
            'filters'          => [
                'max_file_size' => '50mb',
                'mime_types'    => [
                    ['extensions' => '*',],
                ],
            ],
            'flash_swf_url'    => asset('assets/plupload/Moxie.swf'),
            'multipart_params' => [
                '_token' => csrf_token(),
            ],
            'multi_selection'  => false,
            'chunk_size'       => '1mb',
        ];

        $this->options($defaultOptions);
    }

    /**
     * 设置组件的上传地址
     *
     * 由于在构造方法里,还没有form实例,所以放到这个方法,这个方法在组件渲染前执行
     */
    protected function setUploadUrl()
    {
        if (!is_null($this->getOption('url'))) {
            return;
        }
        /**
         * @var $formBuilder Builder
         */
        $formBuilder = $this->form->builder();
        if ($formBuilder->isMode($formBuilder::MODE_CREATE)) {
            $url = $this->form->resource(-1);
        } elseif ($formBuilder->isMode($formBuilder::MODE_EDIT)) {
            $url = $this->form->resource();
        }
        $this->options('url', $url);
    }

    /**
     * 设置文件最大大小的快捷方法(参数示例:'1mb', '500kb')
     *
     * @param string $fileSize
     *
     * @return $this
     */
    public function maxSize($fileSize)
    {
        array_set($this->options, 'filters.max_file_size', $fileSize);

        return $this;
    }

    /**
     * 设置允许的文件后缀快捷方法(参数示例:'jpg,png,jpeg'
     *
     * @param string $fileExt
     *
     * @return $this
     */
    public function ext($fileExt)
    {
        array_set($this->options, 'filters.mime_types.0.extensions', $fileExt);

        return $this;
    }

    /**
     * 设置最多可上传数量的快捷方法
     *
     * @param integer $fileNumber
     *
     * @return $this
     */
    public function maxCount($fileNumber)
    {
        if (!is_numeric($fileNumber) || (int) $fileNumber < 0) {
            throw new \InvalidArgumentException('参数不能小于0');
        }

        $fileNumber = (int) $fileNumber;

        if ($fileNumber != 1) {
            $this->options('multi_selection', true);
            $this->isMultiple = true;
        }

        $this->options('max_num', $fileNumber);

        return $this;
    }

    public function isMultiple()
    {
        return $this->isMultiple;
    }

    /**
     * 前端插件图片预览设置
     *
     * @return void
     */
    protected function setupPreviewOptions()
    {
        $this->options([
            'initialPreview'       => $this->preview(),
            'initialPreviewConfig' => $this->initialPreviewConfig(),
        ]);
    }

    /**
     * 设置文件系统驱动
     *
     * @param string $disk `config/filesystems.php` 中定义的disks
     *
     * @return $this
     */
    public function disk($disk)
    {
        if (!array_key_exists($disk, config('filesystems.disks'))) {
            flash_msg("[$disk] 没有在`config/filesystems.php`中的`disks`键中定义", 'error');

            return;
        }

        $this->diskConfig = config("filesystems.disks.{$disk}");
        $this->storage = \Storage::disk($disk);

        return $this;
    }

    /**
     * 设置存储目录和文件名
     *
     * @param string      $directory
     * @param null|string $name
     *
     * @return $this
     */
    public function move($directory, $name = null)
    {
        $this->dir($directory);

        $this->name($name);

        return $this;
    }

    /**
     * 设置存储目录
     *
     * @param string $dir
     *
     * @return $this
     */
    public function dir($dir)
    {
        if ($dir) {
            $this->directory = $dir;
        }

        return $this;
    }

    /**
     * 设置文件存储名
     *
     * @param string|callable $name
     *
     * @return $this
     */
    public function name($name)
    {
        if ($name) {
            $this->name = $name;
        }

        return $this;
    }

    /**
     * 返回文件的文件名
     *
     * @param UploadedFile $file
     *
     * @return string
     */
    protected function getStoreName(UploadedFile $file)
    {
        if ($this->useUniqueName) {
            return $this->generateUniqueName($file);
        }

        if (is_callable($this->name)) {
            $callback = $this->name->bindTo($this);

            return call_user_func($callback, $file);
        }

        if (is_string($this->name)) {
            return $this->name;
        }

        return $file->getClientOriginalName();
    }

    /**
     * 获取文件的存储目录
     *
     * @return mixed|string
     */
    public function getDirectory()
    {
        if ($this->directory instanceof \Closure) {
            return call_user_func($this->directory, $this->form);
        }

        return $this->directory ?: $this->defaultDirectory();
    }

    /**
     * 返回默认存储目录
     *
     * @return string
     */
    protected function defaultDirectory()
    {
        return '';
    }

    /**
     * 把上传的文件移动到设置好的目录
     *
     * @param UploadedFile $file
     *
     * @return mixed
     */
    protected function upload(UploadedFile $file)
    {
        $this->renameIfExists($file);

        $target = $this->getDirectory().'/'.$this->name;

        $this->storage->put($target, file_get_contents($file->getRealPath()));

        return $target;
    }

    /**
     * 重命名重名文件
     *
     * @param $file
     *
     * @return void
     */
    public function renameIfExists(UploadedFile $file)
    {
        if ($this->storage->exists("{$this->getDirectory()}/$this->name")) {
            $filePathInfo = pathinfo($file->getClientOriginalName());

            $filename = $filePathInfo['filename'].'_'.uniqid();
            $fileSuffix = isset($filePathInfo['extension']) ? ('.'.$filePathInfo['extension']) : '';

            $this->name = $filename.$fileSuffix;
        }
    }

    /**
     * 获取文件的url
     *
     * @param $path
     *
     * @return string
     */
    public function url($path)
    {
        if (\URL::isValidUrl($path)) {
            return $path;
        }

        return rtrim($this->diskConfig['url'], '/').'/'.trim($path, '/');
    }

    public function rootDir()
    {
        return $this->diskConfig['root'];
    }

    /**
     * 生成唯一文件名
     *
     * @param UploadedFile $file
     *
     * @return string
     */
    protected function generateUniqueName(UploadedFile $file)
    {
        return uniqid().'.'.strtolower($file->getClientOriginalExtension());
    }

    /**
     * 如果是更新记录,则更新完文件字段的值之后,删除原来的文件
     *
     * @return void.
     */
    public function destroy()
    {
        if ($this->storage->exists($this->original)) {
            $this->storage->delete($this->original);
        }
    }

    public function render()
    {
        $this->setUploadUrl();

        return parent::render();
    }
}
